package intelligentcity.system;

import intelligentcity.conf.LightsConf;

public abstract class Lights implements Runnable, ILights {

    private final static State[] states = { State.NORTH_SOUTH_GREEN, State.NORTH_SOUTH_YELLOW,
            State.WEST_EAST_RED_YELLOW, State.WEST_EAST_GREEN, State.WEST_EAST_YELLOW, State.NORTH_SOUTH_RED_YELLOW };
    private int[] times = { LightsConf.timeOfPassableState, LightsConf.timeOfImpassableState,
            LightsConf.timeOfImpassableState, LightsConf.timeOfPassableState, LightsConf.timeOfImpassableState,
            LightsConf.timeOfImpassableState };
    private int pos;

    private LightColor north;
    private LightColor south;
    private LightColor west;
    private LightColor east;

    private volatile int timeNS;
    private volatile int timeWE;

    public Lights() {
        this(0);
    }

    public Lights(int numberOfState) {
        pos = numberOfState - 1;
        timeNS = times[0];
        timeWE = times[3];
    }

    @Override
    public final void run() {
        while (true) {
            changeState(next());
            changeTimes();
            times[0] = timeNS = validateTime(timeNS);
            times[3] = timeWE = validateTime(timeWE);
            try {
                Thread.sleep(times[pos]);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    protected abstract void changeTimes();

    protected synchronized int getTimeWE() {
        return timeWE;
    }

    protected synchronized void setTimeNS(int timeNS) {
        this.timeNS = timeNS;
    }

    protected synchronized void setTimeWE(int timeWE) {
        this.timeWE = timeWE;
    }

    protected synchronized int getTimeNS() {
        return timeNS;
    }

    @Override
    public final synchronized LightColor getNorth() {
        return north;
    }

    @Override
    public final synchronized LightColor getSouth() {
        return south;
    }

    @Override
    public final synchronized LightColor getWest() {
        return west;
    }

    @Override
    public final synchronized LightColor getEast() {
        return east;
    }

    private int validateTime(int time) {
        if (time < LightsConf.minTimeOfPassableState) {
            return LightsConf.minTimeOfPassableState;
        }
        if (time > LightsConf.maxTimeOfPassableState) {
            return LightsConf.maxTimeOfPassableState;
        }
        return time;
    }

    private State next() {
        if (++pos >= states.length)
            pos = 0;
        return states[pos];
    }

    private final void changeState(State state) {
        switch (state) {

            case NORTH_SOUTH_GREEN:
                north = LightColor.GREEN;
                south = LightColor.GREEN;
                west = LightColor.RED;
                east = LightColor.RED;
                break;

            case NORTH_SOUTH_YELLOW:
                north = LightColor.YELLOW;
                south = LightColor.YELLOW;
                west = LightColor.RED;
                east = LightColor.RED;
                break;

            case WEST_EAST_RED_YELLOW:
                north = LightColor.RED;
                south = LightColor.RED;
                west = LightColor.RED_YELLOW;
                east = LightColor.RED_YELLOW;
                break;

            case WEST_EAST_GREEN:
                north = LightColor.RED;
                south = LightColor.RED;
                west = LightColor.GREEN;
                east = LightColor.GREEN;
                break;

            case WEST_EAST_YELLOW:
                north = LightColor.RED;
                south = LightColor.RED;
                west = LightColor.YELLOW;
                east = LightColor.YELLOW;
                break;

            case NORTH_SOUTH_RED_YELLOW:
                north = LightColor.RED_YELLOW;
                south = LightColor.RED_YELLOW;
                west = LightColor.RED;
                east = LightColor.RED;
                break;
        }
    }

}
